package com.handinfo.android.core.net;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Vector;

import com.handinfo.android.utils.Tools;

/**
 * @author Xia Mingwei
 * @version 1.0
 */
public abstract class BaseConnection implements Runnable
{
	public static final int CONNECTION_TYPE_TCP = 1;
	public static final int CONNECTION_TYPE_HTTP = 2;
	public static final int PACKAGE_HEADER_LENGTH = 10;
	public static final int PACKAGE_RECEIVE_HEADER_LENGTH = 10;
	protected String m_url = null;
	protected String m_server = null;
	protected int m_port;
	protected boolean m_alive;
	protected IConnectionHandler m_handler = null;
	protected Socket m_socket = null;
	protected DataInputStream m_is;
	protected DataOutputStream m_os;
	protected String m_username;
	protected int m_curPackType;
	protected int m_curPackLength;
	protected Vector<byte[]> m_requests = new Vector<byte[]>(5, 5);
	protected Vector<Body> m_receives = new Vector<Body>(5, 5);
	protected int m_sum;
	protected boolean m_idle;

	/**
	 * @param type
	 * @param server
	 * @param port
	 * @param arg
	 * @param receiver
	 * @return sfge.BaseConnection
	 * @roseuid 431FBC8001E4
	 */
	static public BaseConnection createConnection(int type, String server,
			int port, IConnectionHandler receiver)
	{
		BaseConnection connection = null;
		connection = new TcpConnection(server, port, receiver);
		return connection;
	}

	/**
	 * @param b
	 * byte[]
	 * @param off
	 * int
	 * @param num
	 * int
	 * @return int
	 */
	public void clearBuf()
	{
		if (!m_requests.isEmpty()) {
			m_requests.removeAllElements();
		}
		if (!m_receives.isEmpty()) {
			m_receives.removeAllElements();
		}
	}

	/**
	 * @roseuid 431FA8E000FA
	 */
	protected BaseConnection()
	{
	}

	public void setHanlder(IConnectionHandler handler)
	{
		m_handler = handler;
	}

	public void open()
	{
		// TimeOut.getInstance().start();
	}

	public void setServiceInfo(String ip, int port)
	{
		
	}

	protected void closeStreams()
	{
		try {
			if (m_is != null) {
				m_is.close();
				m_is = null;
			}
			if (m_os != null) {
				m_os.close();
				m_os = null;
			}
			if (m_socket != null) {
				m_socket.close();
				m_socket = null;
			}
			
			if (m_receives != null) {
				m_receives.removeAllElements();
			}
			if (m_requests != null) {
				m_requests.removeAllElements();
			}
		}
		catch (Exception e) {
			Tools.debug(e);
		}
	}

	public void close()
	{
		closeStreams();
		m_alive = false;
	}

	public void setUsername(String name)
	{
		m_username = name;
	}

	public void keepAlive()
	{
	}

	public boolean isAlive()
	{
		return m_alive;
	}

	private byte[] lock1 = new byte[0];
	private byte[] lock2 = new byte[0];

	protected void addRequest(byte[] msg)
	{
		m_requests.addElement(msg);
		synchronized (lock1) {
			lock1.notifyAll();
		}
	}

	protected byte[] getRequest() throws InterruptedException
	{
		while (m_requests.isEmpty()) {
			synchronized (lock1) {
				lock1.wait();
			}
		}
		byte[] msg = null;
		if (!m_requests.isEmpty()) {
			msg = (byte[]) m_requests.elementAt(0);
			m_requests.removeElementAt(0);
		}
		else {
			Tools.debugPrintln("BaseConnection getRequest() error");
		}
		return msg;
	}

	protected void addReceive(Body body)
	{
		m_receives.addElement(body);
		synchronized (lock2) {
			lock2.notifyAll();
		}
	}

	protected Body getReceive() throws InterruptedException
	{
		while (m_receives.isEmpty()) {
			synchronized (lock2) {
				lock2.wait();
			}
		}
		Body body = null;
		if (!m_receives.isEmpty()) {
//			Tools.debugPrintln("Recver.Length = " + m_receives.size());
			body = (Body) m_receives.elementAt(0);
			m_receives.removeElementAt(0);
		}
		else {
			Tools.debugPrintln("BaseConnection getReceive() error");
		}
		return body;
	}

	/**
	 * @param type
	 * int
	 * @param length
	 * int
	 * @param body
	 * byte[]
	 * @return boolean
	 */
	public abstract boolean sendMessage(int type, int order, byte[] body);

	/**
	 * @param destID
	 * @param type
	 * int
	 * @param length
	 * int
	 * @param body
	 * byte[]
	 * @return boolean
	 */
	public abstract boolean sendMessage(int destId, int type, int order,
			byte[] body);

	/**
	 * @param buffer
	 * byte[]
	 * @param length
	 * int
	 * @return int
	 */
	public static short checkSum(byte[] buffer)
	{
		int sum = 0, i;
		if (buffer.length == 0 || buffer == null) {
			return 0;
		}

		ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
		DataInputStream dis = new DataInputStream(bais);

		try {
			for (i = 0; i < buffer.length / 2; i++) {
				int s = dis.readUnsignedShort();
				sum += (s & 0x00ffff);
			}
			if (i * 2 < buffer.length) {
				sum += buffer[buffer.length - 1] & 0x00ff;
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			try {
				dis.close();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
			try {
				bais.close();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}

		sum = (sum >> 16) + (sum & 0x00ffff);
		sum += (sum >> 16);

		return (short) ~sum;
	}

	public static int s_sessionID = -1;
	public static int s_recvOrderID = 0; // 接受指令号
	public static int s_perseOrderID = 0; //解析指令号
	public static byte s_version = 1; // 协议版本号
	public static int s_sendPackageId = 0; // 发送包号：自动不断累计
	public static int s_recvPackageId = -1; // 接受包号
	public static int s_bodyLength = 0; // 包体长度

	/**
	 * 封包消息（包括协议头和协议体）
	 * 
	 * @param destId
	 * @param type
	 * @param order
	 * @param body
	 * @return
	 */
	public byte[] packageMessage(int destId, int type, int order, byte[] body)
	{
		int length = PACKAGE_HEADER_LENGTH + body.length;
		byte[] pack = new byte[length];
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bos);
		try {
			dos.writeByte(s_version);
//			dos.writeInt(s_sendPackageId++);
			dos.writeByte(destId);
			dos.writeInt(order);
			dos.writeInt(body.length);
			byte[] head = bos.toByteArray();
			System.arraycopy(head, 0, pack, 0, PACKAGE_HEADER_LENGTH);
			System.arraycopy(body, 0, pack, PACKAGE_HEADER_LENGTH, body.length);
			dos.close();
			bos.close();
		}
		catch (IOException ex) {
			ex.printStackTrace();
		}
		finally {
			dos = null;
			bos = null;
		}
		return pack;
	}

	/**
	 * 解析包
	 * 
	 * @param header
	 * byte[]
	 * @return boolean
	 */
	public boolean unpackageMessage(byte[] header)
	{
		int packageId, length, version, destId;
		length = 0;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(header);
			DataInputStream dis = new DataInputStream(bis);
			version = dis.readByte(); // 协议版本号
//			packageId = dis.readInt(); // 接受包Id
			destId = dis.readByte(); // 目标Id
			s_recvOrderID = dis.readInt();// 指令号
			s_bodyLength = dis.readInt(); // 长度
			// Tools.debugPrintln("unpackageMessage packageId : " + packageId);
			// 包的ID异常
//			if (packageId <= s_recvPackageId) { // 重复收到包
//				throw new Exception();
//			}
//			else {
//				s_recvPackageId = packageId; // 正常的包Id，记录下来
//			}
			// 发送目标Id不是客户端
			if (destId != DEST_CLIENT) {
				throw new Exception();
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
			return false;
		}
		m_curPackLength = length;
		return true;
	}

	/**
	 * 解析包体
	 * 
	 * @param body
	 * @throws Exception
	 */
	public void perseBody(byte[] body)
	{
		if (m_handler != null) {
			m_handler.recvMessage(0, body);
		}
	}

	boolean check(byte[] b, int length)
	{
		int mysum;
		mysum = checkSum(b);
		if (m_sum != mysum) {
			return false;
		}
		return true;
	}

	// 目标服务器ID
	public static final int DEST_CLIENT = 1;
	public static final int DEST_DISTRICT = 2;
	public static final int DEST_GATEWAY = 3;//
	public static final int DEST_WORLD = 4;
	public static final int DEST_UNIVERSE = 5;
	public static final int DEST_PUB = 6;
	public static final int DEST_AUTH = 7;
	public static final int DEST_RES = 8;
	public static final int DEST_INS = 9;

	class Body
	{
		public int mId;
		public byte[] mData;

		public Body(int id, byte[] data)
		{
			mId = id;
			mData = data;
		}
		
		public String toString()
		{
			return "mId : " + mId;
		}

		public void dispose()
		{
			mData = null;
		}
	}
}
